# 多维度输入，样本有多个特征，不再是单一维度

import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt

# 1、构建数据集
# 加载文件中的数据，每一行为一个样本的特征及标签，最后一列为样本的标签
xy = np.loadtxt('../datasets/diabetes.csv.gz', delimiter=',', dtype=np.float32)
# 读取样本特征，最后一列数据不读取，构建样本特征数据集
x_data = torch.from_numpy(xy[:, :-1])
# 读取最后一列元素，构建标签数据集
y_data = torch.from_numpy(xy[:, [-1]])


# 2、定义模型，继承torch.nn.Module
# 2.1、实现 __init__ 函数，用于变量的初始化，第一行调用父类的 __init__
# 2.2、定义前馈计算 forward() 函数

class MultiDimInputModel(nn.Module):
    def __init__(self):
        """
        设置参数，并进行初始化
        """
        # 调用父类的 __init__函数
        super(MultiDimInputModel, self).__init__()
        # 定义线性模型，nn.Linear(in_features,out_features,bias) 分别是输入样本的特征维度，输出结果维度，是否使用偏执项，默认使用。
        # 定义多个线性层
        self.linear1 = nn.Linear(8, 6)
        self.linear2 = nn.Linear(6, 4)
        self.linear3 = nn.Linear(4, 1)
        # 定义一个激活函数，这里使用torch自带的 sigmoid 函数作为激活函数
        self.activate = nn.Sigmoid()
        # self.activate = nn.ReLU()
        # self.activate = nn.Tanh()
        # self.activate = nn.LeakyReLU()

    # 定义前馈计算，forward是一个callable函数，在父类中实现，这里是对父类中 forward 函数的重载
    def forward(self, x):
        # 第一次线性变换之后使用 sigmoid 激活
        x = self.activate(self.linear1(x))
        # 第二次线性变换之后使用 sigmoid 激活
        x = self.activate(self.linear2(x))
        # 第三次线性变换之后激活，得到 y_pred
        y_pred = self.activate(self.linear3(x))
        return y_pred


# 得到模型
model = MultiDimInputModel()

# 定义损失函数，使用你二分类交叉熵损失，二进制交叉熵损失要求进行计算时的 y_pred 必须在[0,1]范围内
# criterion = torch.nn.BCELoss(size_average=True)
# 使用这个参数，size_average 过时
criterion = torch.nn.BCELoss(reduction='mean')
# 使用随机梯度优化算法，传入需要学习的参数和学习率
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

# 这次训练时并没有使用MIni-batch算法，而是每一个 epoch 更新一次参数
for epoch in range(100):
    # 前馈计算
    y_pred = model(x_data)
    loss = criterion(y_pred, y_data)
    print(epoch, loss.item())

    # 优化器梯度清0
    optimizer.zero_grad()
    # 反向传播
    loss.backward()
    # 更新参数
    optimizer.step()

